﻿using System;
using System.Collections.Generic;
using System.Security.Permissions;

using Moe.ECamel.Common.Contracts;
using Moe.ECamel.Common.Entities;
using Moe.ECamel.Common.Security;
using Moe.ECamel.Server.BL;

namespace Moe.ECamel.Server.Services
{
	/// <summary>
	/// Service Interface for Subscription services.
	/// </summary>
	public class SubscriptionService : ECamelService, ISubscriptionService
	{
		public SubscriptionService()
			: base()
		{
		}

		public SubscriptionService(ECamelService caller)
			: base(caller)
		{
		}

		#region ISubscriptionService Members

		[PrincipalPermission(SecurityAction.Demand, Role = Operations.SubscriptionView)]
		public List<Subscription> GetSubscriptionsByCustomer(int customerId)
		{
			// Execute operation via service BL:
			using (var bl = new SubscriptionBL(GetReadOnlyDataContext()))
			{
				return bl.GetSubscriptionsByCustomer(customerId);
			}
		}
		
		[PrincipalPermission(SecurityAction.Demand, Role = Operations.SubscriptionView)]
		public Subscription GetActiveSubscriptionOfCustomer(int customerId)
		{
			using (var bl = new SubscriptionBL(GetReadOnlyDataContext()))
			{
				return bl.GetActiveSubscriptionOfCustomer(customerId);
			}
		}
		
		[PrincipalPermission(SecurityAction.Demand, Role = Operations.SubscriptionAdd)]
		public ECamelError CreateSubscription(Subscription subscription)
		{
			return CreateSubscriptions(new Subscription[] {subscription});
		}

		[PrincipalPermission(SecurityAction.Demand, Role = Operations.SubscriptionAdd)]
		public ECamelError CreateSubscriptions(IEnumerable<Subscription> subscriptions)
		{
			// Generate subscriptions payments:
			var payments = new List<Payment>();
			foreach (var subscription in subscriptions)
			{
				subscription.PricePayment = CreatePricePayment(subscription);
				payments.Add(subscription.PricePayment);
				subscription.DepositPayment = CreateDepositPayment(subscription);
				payments.Add(subscription.DepositPayment);
			}

			using (var paymentService = new PaymentService(this))
			{
				paymentService.AddPayments(payments);
			}

			using (var bl = new SubscriptionBL(GetReadWriteDataContext()))
			{
				var error = bl.CreateSubscriptions(subscriptions);
				if (error != null)
					return error;
			}

			SubmitChanges();
			return null;
		}

		[PrincipalPermission(SecurityAction.Demand, Role = Operations.SubscriptionEdit)]
		public ECamelError UpdateSubscriptionStatus(int subscriptionId, bool active)
		{
			ECamelError error;

			using (var bl = new SubscriptionBL(GetReadWriteDataContext()))
			{
				error = bl.UpdateSubscriptionStatus(subscriptionId, active);
			}

			return SubmitChanges(error);
		}

		[PrincipalPermission(SecurityAction.Demand, Role = Operations.SubscriptionAdd)]
		public ECamelError RenewSubscription(int subscriptionId, DateTime renewalDate)
		{
			// Get old subscription:
			Subscription oldSubscription;
			using (var bl = new SubscriptionBL(GetReadOnlyDataContext()))
			{
				oldSubscription = bl.GetSubscriptionById(subscriptionId);
			}

			// Create a new subscription for the same customer of the same type with a new date:
			var newSubscription = new Subscription();
			newSubscription.CustomerId = oldSubscription.CustomerId;
			newSubscription.InitializeNewSubscription(oldSubscription.SubscriptionType, renewalDate);
			// Copy quantities:
			for (int i = 0; i < oldSubscription.SubscriptionItemTypeInfos.Count; i++)
				newSubscription.SubscriptionItemTypeInfos[i].Quantity = oldSubscription.SubscriptionItemTypeInfos[i].Quantity;

			// Create a new price payment, reuse deposit payment:
			newSubscription.PricePayment = CreatePricePayment(newSubscription);
			newSubscription.DepositPaymentId = oldSubscription.DepositPaymentId;

			using (var paymentService = new PaymentService(this))
			{
				paymentService.AddPayments(new Payment[] {newSubscription.PricePayment});
			}


			using (var bl = new SubscriptionBL(GetReadWriteDataContext()))
			{
				var error = bl.CreateSubscriptions(new Subscription[] {newSubscription});
				if (error != null)
					return error;
			}

			SubmitChanges();
			return null;
		}

		private static Payment CreatePricePayment(Subscription subscription)
		{
			return new Payment
			{
				CustomerId = subscription.CustomerId,
				PayedOn = subscription.ActivationDate,
				Amount = subscription.TotalPrice,
				Description = "תשלום עבור מנוי " + subscription.SubscriptionTypeName
			};
		}
		private static Payment CreateDepositPayment(Subscription subscription)
		{
			return new Payment
			{
				CustomerId = subscription.CustomerId,
				PayedOn = subscription.ActivationDate,
				Amount = subscription.TotalDeposit,
				Description = "פקדון עבור מנוי " + subscription.SubscriptionTypeName
			};
		}

		public IList<SubscriptionType> GetSubscriptionTypes()
		{
			using (var bl = new SubscriptionBL(GetReadOnlyDataContext()))
			{
				return bl.GetSubscriptionTypes();
			}
		}

		#endregion

		public void ProcessExpiredSubscriptions(DateTime date)
		{
			IList<Subscription> expiredSubscriptions;
			using (var bl = new SubscriptionBL(GetReadWriteDataContext()))
			{
				expiredSubscriptions = bl.GetExpiredSubscriptions(date);
			}

			using (var mailMessageService = new MailMessageService(this))
			{
				foreach (var subscription in expiredSubscriptions)
				{
					// Deactivate subscription:
					subscription.Active = false;
					// Send mail to customer to inform that the subscription has been deactivated.
					var subject = "הודעה על תפוגת תוקף מנוי";
					var body =
						"הרינו להודיעך כי בתאריך " + date.ToLongDateString() + " פג תוקפו של המנוי שלך בספריה הארצית." + Environment.NewLine +
						"אם ברצונך להמשיך להשתמש בשרותי הספריה, עליך לחדש את המנוי או לרכוש מנוי חדש." + Environment.NewLine +
						"הסכום לתשלום עבור חידוש המנוי הוא " + subscription.TotalPrice + " שקלים חדשים, כאשר הפקדון מהמנוי הישן יועבר כפקדון למנוי החדש.";
					var mailMessage = new MailMessage(subscription.Customer, date, subject, body);
					mailMessageService.SendMessage(mailMessage);
				}
			}

			SubmitChanges();
		}
	}
}